package dowmload

import (
	"errors"
	"flag"
	"fmt"
	"gvms/common"
	"gvms/file"
	"gvms/systemRegistry"
	"gvms/util"
	"gvms/web"
	"io"
	"io/fs"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"sync"
	"time"
)

// 下载任务配置
type DownloadConfig struct {
	URL         string        // 下载URL
	OutputPath  string        // 输出路径
	ChunkSize   int64         // 每个分片的大小(字节)
	Concurrency int           // 并发数
	RetryTimes  int           // 重试次数
	Timeout     time.Duration // 请求超时时间
}

// 下载分片
type DownloadChunk struct {
	Index int
	Start int64
	End   int64
	Path  string
	Retry int
	Err   error
}

// 下载文件
func downloadFile(config DownloadConfig) error {
	// 创建临时目录
	tempDir, err := createTempDir(config.OutputPath)
	if err != nil {
		return fmt.Errorf("创建临时目录失败: %v", err)
	}
	defer os.RemoveAll(tempDir) // 下载完成后清理临时文件

	// 获取文件信息
	fileSize, err := getFileSize(config.URL)
	if err != nil {
		fmt.Printf("获取分片失败")
		b := web.Download(config.URL, config.OutputPath)
		if !b {
			return fmt.Errorf("下载失败")
		}
	}

	// 计算分片
	chunks := createChunks(fileSize, config.ChunkSize, tempDir)
	if len(chunks) == 0 {
		return errors.New("没有需要下载的分片")
	}

	// 并发下载分片
	var wg sync.WaitGroup
	chunkQueue := make(chan DownloadChunk, len(chunks))
	resultQueue := make(chan DownloadChunk, len(chunks))

	// 启动工作协程
	for i := 0; i < config.Concurrency; i++ {
		wg.Add(1)
		go downloadWorker(&wg, chunkQueue, resultQueue, config)
	}

	// 发送分片任务
	go func() {
		for _, chunk := range chunks {
			chunkQueue <- chunk
		}
		close(chunkQueue)
	}()

	// 等待所有任务完成
	go func() {
		wg.Wait()
		close(resultQueue)
	}()

	// 处理结果
	for result := range resultQueue {
		if result.Err != nil {
			return fmt.Errorf("下载分片 %d 失败: %v", result.Index, result.Err)
		}
	}

	// 合并分片
	if err := mergeChunks(chunks, config.OutputPath); err != nil {
		return fmt.Errorf("合并分片失败: %v", err)
	}

	return nil
}

// 创建临时目录
func createTempDir(outputPath string) (string, error) {
	dir := filepath.Dir(outputPath)
	base := filepath.Base(outputPath)
	tempDir := filepath.Join(dir, fmt.Sprintf(".%s.temp", base))

	if err := os.MkdirAll(tempDir, 0755); err != nil {
		return "", err
	}

	return tempDir, nil
}

// 获取文件大小
func getFileSize(url string) (int64, error) {
	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Head(url)
	if err != nil {
		return 0, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return 0, fmt.Errorf("请求失败: %s", resp.Status)
	}

	size, ok := resp.ContentLength, resp.Header.Get("Accept-Ranges")
	if size <= 0 || ok != "bytes" {
		return 0, errors.New("服务器不支持分片下载")
	}

	return size, nil
}

// 创建分片
func createChunks(fileSize, chunkSize int64, tempDir string) []DownloadChunk {
	var chunks []DownloadChunk
	if fileSize <= 0 || chunkSize <= 0 {
		return chunks
	}

	chunkCount := (fileSize + chunkSize - 1) / chunkSize
	for i := int64(0); i < chunkCount; i++ {
		start := i * chunkSize
		end := start + chunkSize - 1
		if end >= fileSize {
			end = fileSize - 1
		}

		chunks = append(chunks, DownloadChunk{
			Index: int(i),
			Start: start,
			End:   end,
			Path:  filepath.Join(tempDir, fmt.Sprintf("chunk_%d", i)),
		})
	}

	return chunks
}

// 下载工作协程
func downloadWorker(wg *sync.WaitGroup, jobs <-chan DownloadChunk, results chan<- DownloadChunk, config DownloadConfig) {
	defer wg.Done()

	client := &http.Client{Timeout: config.Timeout}

	for chunk := range jobs {
		// 复制分片避免竞争
		currentChunk := chunk
		var err error

		for currentChunk.Retry <= config.RetryTimes {
			err = downloadSingleChunk(client, currentChunk, config.URL)
			if err == nil {
				break
			}

			currentChunk.Retry++
			time.Sleep(time.Duration(currentChunk.Retry*2) * time.Second) // 指数退避
		}

		currentChunk.Err = err
		results <- currentChunk
	}
}

// 下载单个分片
func downloadSingleChunk(client *http.Client, chunk DownloadChunk, url string) error {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}

	req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", chunk.Start, chunk.End))

	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusPartialContent {
		return fmt.Errorf("请求分片失败: %s", resp.Status)
	}

	outFile, err := os.Create(chunk.Path)
	if err != nil {
		return err
	}
	defer outFile.Close()

	_, err = io.Copy(outFile, resp.Body)
	return err
}

// 合并分片
func mergeChunks(chunks []DownloadChunk, outputPath string) error {
	outFile, err := os.Create(outputPath)
	if err != nil {
		return err
	}
	defer outFile.Close()

	// 按索引排序
	for i := 0; i < len(chunks); i++ {
		chunkFile, err := os.Open(chunks[i].Path)
		if err != nil {
			return err
		}

		_, err = io.Copy(outFile, chunkFile)
		chunkFile.Close()

		if err != nil {
			return err
		}
	}

	return nil
}
func Download(goHome *string, jdkVersions util.JDKVersion, msg func(m string)) {
	downloadUrl := jdkVersions.Url
	filename := common.ExtractFilename(downloadUrl)
	dir, _ := os.Getwd()
	downloadDir := filepath.Join(dir, "\\download")
	exists, _, _ := common.DirectoryExists(downloadDir)
	if !exists {
		os.MkdirAll(downloadDir, 0777)
	}
	downloadFilePath := filepath.Join(downloadDir, "\\", filename)
	exist, iDir, _ := common.DirectoryExists(downloadFilePath)
	if !exist || iDir {
		for i := 0; i < 3; i++ {
			err := download(downloadUrl, downloadFilePath)
			if err == nil {
				break
			}
		}
	}
	home := filepath.Dir(*goHome)
	if home == "C:\\" || home == "C:\\Program Files" {
		return
	}
	removeDirectories(home)
	file.UnzipJ(downloadFilePath, home, msg)
	recursive, err := getDirectoriesNonRecursive(home)
	if err != nil {
		fmt.Printf("获取目录失败: %v\n", err)
		return
	}
	*goHome = recursive[0]
	systemRegistry.SetSysGoHome(*goHome)
}

// 删除指定目录下的所有子目录（保留文件）
func removeDirectories(root string) error {
	// 遍历目录
	return filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}

		// 如果是目录且不是根目录本身
		if d.IsDir() && path != root {
			fmt.Printf("删除目录: %s\n", path)
			return os.RemoveAll(path) // 递归删除目录及其内容
		}

		return nil
	})
}

// 非递归版本（仅一级子目录）
func getDirectoriesNonRecursive(root string) ([]string, error) {
	var dirs []string

	// 打开目录
	entries, err := os.ReadDir(root)
	if err != nil {
		return nil, err
	}

	// 遍历目录项
	for _, entry := range entries {
		if entry.IsDir() {
			dirs = append(dirs, filepath.Join(root, entry.Name()))
		}
	}

	return dirs, nil
}

func download(url, path string) error {
	cpu := runtime.NumCPU()
	uri := flag.String("url", url, "下载URL")
	output := flag.String("output", path, "输出文件路径")
	concurrency := flag.Int("concurrency", cpu*3, "并发数")
	chunkSize := flag.Int64("chunk-size", 10*1024*1024, "分片大小(字节)")
	retry := flag.Int("retry", 3, "重试次数")
	timeout := flag.Int("timeout", 30, "超时时间(秒)")

	flag.Parse()

	if *uri == "" {
		fmt.Println("请指定下载URL (-url)")
		flag.Usage()
		return errors.New("请指定下载URL (-url)")
	}

	config := DownloadConfig{
		URL:         *uri,
		OutputPath:  *output,
		ChunkSize:   *chunkSize,
		Concurrency: *concurrency,
		RetryTimes:  *retry,
		Timeout:     time.Duration(*timeout) * time.Second,
	}

	start := time.Now()
	fmt.Printf("开始下载: %s\n", *uri)

	if err := downloadFile(config); err != nil {
		fmt.Printf("下载失败: %v\n", err)
		return err
	}

	elapsed := time.Since(start)
	fmt.Printf("下载完成: %s (耗时: %v)\n", *output, elapsed)
	return nil
}
